package com.hzw.saas.web.admin.security;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.hzw.saas.api.rbac.IUserRoleService;
import com.hzw.saas.api.rbac.enums.PermSysEnum;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.security.pojo.dto.SaasUser;
import com.hzw.saas.service.rbac.config.RbacConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * <p>
 * 权限校验服务
 * </p>
 *
 * @author sonam
 * @since 2021/2/8 15:49
 */
@Slf4j
@Component(PermissionService.BEAN_NAME)
@RequiredArgsConstructor
public class PermissionService {

    public static final String BEAN_NAME = "pms";

    private final RbacConfig rbacConfig;

    private final IUserRoleService userRoleService;

    /**
     * 判断是否含有任一权限
     *
     * @param perms 待校验权限
     * @return {boolean}
     */
    public boolean assertAny(String... perms) {
        boolean match = this.hasAny(perms);
        if (match) {
            return true;
        } else {
            throw DefaultSaasException.build("无请求权限").status(HttpStatus.FORBIDDEN);
        }
    }

    /**
     * 判断是否含有所有权限
     *
     * @param perms 待校验权限
     * @return {boolean}
     */
    public boolean assertAll(String... perms) {
        boolean match = this.hasAll(perms);
        if (match) {
            return true;
        } else {
            throw DefaultSaasException.build("无请求权限").status(HttpStatus.FORBIDDEN);
        }
    }

    /**
     * 判断是否含有任一权限
     *
     * @param perms 待校验权限
     * @return {boolean}
     */
    public boolean hasAny(String... perms) {
        if (ArrayUtil.isEmpty(perms)) {
            return true;
        }
        // authentication
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (Objects.isNull(auth)) {
            return false;
        }
        SaasUser user = auth.getPrincipal() instanceof SaasUser ? (SaasUser) auth.getPrincipal() : null;
        if (Objects.isNull(user)) {
            return false;
        }
        // super administrator
        if (this.isSuperAdmin(user.getUserId())) {
            return true;
        }
        return this.getAuthoritiesStream(user.getUserId())
            .anyMatch(x -> PatternMatchUtils.simpleMatch(perms, x));
    }

    /**
     * 判断是否含有所有权限
     *
     * @param perms 待校验权限
     * @return {boolean}
     */
    public boolean hasAll(String... perms) {
        if (ArrayUtil.isEmpty(perms)) {
            return true;
        }
        // authentication
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (Objects.isNull(auth)) {
            return false;
        }
        SaasUser user = auth.getPrincipal() instanceof SaasUser ? (SaasUser) auth.getPrincipal() : null;
        if (Objects.isNull(user)) {
            return false;
        }
        // super administrator
        if (this.isSuperAdmin(user.getUserId())) {
            return true;
        }
        for (String perm : perms) {
            if (StrUtil.isBlank(perm)) {
                continue;
            }
            boolean match = this.getAuthoritiesStream(user.getUserId())
                .anyMatch(x -> PatternMatchUtils.simpleMatch(perm, x));
            if (!match) {
                return false;
            }
        }
        return true;
    }

    /**
     * 检查是否为超级管理员
     *
     * @param auth
     * @return
     */
    private boolean isSuperAdmin(Authentication auth) {
        SaasUser user = auth.getPrincipal() instanceof SaasUser ? (SaasUser) auth.getPrincipal() : null;
        return rbacConfig.getSuperAdminMode() &&
            Objects.nonNull(user) &&
            Objects.equals(user.getUserId(), rbacConfig.getSuperAdminId());
    }

    private boolean isSuperAdmin(String userId) {
        return rbacConfig.getSuperAdminMode() &&
            Objects.nonNull(userId) &&
            Objects.equals(userId, rbacConfig.getSuperAdminId());
    }

    private Stream<String> getAuthoritiesStream(Authentication auth) {
        return auth.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .filter(StringUtils::hasText);
    }

    private Stream<String> getAuthoritiesStream(String userId) {
        List<String> strings = userRoleService.listAuthorities(PermSysEnum.ADMIN.getCode(), userId);
        return strings.stream().filter(StringUtils::hasText);
    }

}
